package com.raph.db;

import com.raph.exception.PasswordErrorException;
import com.raph.exception.UserNotExistException;
import com.raph.user.User;
import org.mindrot.jbcrypt.BCrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import static com.raph.constant.Constant.*;

public class DbUser {
    private static final Logger logger = LoggerFactory.getLogger(DbUser.class);
    private static volatile DbUser INSTANCE;

    private DbUser() {
    }

    public static DbUser getInstance() {
        if(INSTANCE == null) {
            synchronized (DbUser.class) {
                if(INSTANCE == null) {
                    INSTANCE = new DbUser();
                }
            }
        }

        return INSTANCE;
    }

    public void add(User user) throws SQLException {
        String sql = "INSERT INTO user(uid,username,password) VALUES(?,?,?)";
        Connection conn = DbUtils.getConnection();
        if(conn == null) {
            throw new SQLException(GET_SQL_CONNECTION_FAILED);
        }

        try (PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setString(1, user.getUid());
            ps.setString(2, user.getUsername());
            String hashPassword = BCrypt.hashpw(user.getPassword(), BCrypt.gensalt());
            ps.setString(3, hashPassword);
            ps.executeUpdate();
        }

        DbUtils.close(conn);
    }

    public User checkLogin(String keyword, String password) throws SQLException, UserNotExistException {
        String sql = "SELECT uid, username, password FROM user " +
                "WHERE uid = ? OR username = ?";
        Connection conn = DbUtils.getConnection();
        if(conn == null) {
            throw  new SQLException(GET_SQL_CONNECTION_FAILED);
        }

        List<User> list = new ArrayList<>();
        try (PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setString(1, keyword);
            ps.setString(2, keyword);
            ResultSet rs = ps.executeQuery();
            while(rs.next()) {
                User user = new User();
                user.setUsername(rs.getString("username"));
                user.setUid(rs.getString("uid"));
                user.setPassword(rs.getString("password"));
                list.add(user);
            }
        }

        DbUtils.close(conn);

        if(list.isEmpty()) {
            throw new UserNotExistException();
        }

        for(var i : list) {
            if(BCrypt.checkpw(password, i.getPassword())) {
                i.setPassword(password);
                return i;
            }
        }

        throw new PasswordErrorException();
    }

    public List<User> getByUsername(String username) throws SQLException {
        String sql = "SELECT uid,username FROM user WHERE username = ?";
        Connection conn = DbUtils.getConnection();
        if(conn == null) {
            throw new SQLException(GET_SQL_CONNECTION_FAILED);
        }

        List<User> list = new ArrayList<>();
        try(PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setString(1, username);
            ResultSet rs = ps.executeQuery();
            while(rs.next()) {
                list.add(mapUser(rs));
            }
        }

        DbUtils.close(conn);
        return list;
    }

    private User mapUser(ResultSet rs) throws SQLException {
        User user = new User();
        user.setUid(rs.getString("uid"));
        user.setUsername(rs.getString("username"));
        return user;
    }
}
